package cn.anxingongyou.anxin.controller;

import cn.anxingongyou.anxin.entity.Wages;
import cn.anxingongyou.anxin.entity.wxpay.Configure;
import cn.anxingongyou.anxin.entity.wxpay.OrderReturnInfo;
import cn.anxingongyou.anxin.service.WagesService;
import cn.anxingongyou.anxin.utils.Result;
import cn.anxingongyou.anxin.utils.wxsdk.WXPayConstants;
import cn.anxingongyou.anxin.utils.wxsdk.WXPayUtil;
import cn.anxingongyou.anxin.utils.wxsdk.utils.HttpRequest;
import cn.anxingongyou.anxin.utils.wxsdk.utils.RandomStringGenerator;
import cn.anxingongyou.anxin.utils.wxsdk.utils.getUserIP;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.thoughtworks.xstream.XStream;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;


import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

@RestController
public class wxPayController {

    private static final Logger L = LoggerFactory.getLogger(wxPayController.class);

    private final WagesService wagesService;

    @Value("${wxpay.baseNotifyUrl}")
    private String baseNotifyUrl;
    //支付回调地址（你自己的请求地址，可以自己随意配置啦，写在这方便你理解）
    private String notify_url = "/weixin/wages/callback/wxNotify";
    private String notify_url2 = "/weixin/member/callback/wxNotify";
    //交易类型（这里是小程序）
    private final String trade_type = "JSAPI";
    //统一下单API接口链接（微信官方的接口）
    private final String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    @Autowired
    public wxPayController(WagesService wagesService) {
        this.wagesService = wagesService;
    }


    @ApiOperation("使用打卡记录（wages）发起微信支付")
    @ApiResponses({
            @ApiResponse(code = 200,message = "查询成功"),
            @ApiResponse(code = 500,message = "查询失败")
    })
    @RequestMapping(value = "/weixin/wages/pay",method = RequestMethod.GET)
    public Map wxPayToTest(
            HttpServletRequest request,
            @RequestParam("money") @ApiParam(value = "现金数量", required = true) String money,
            @RequestParam("openid") @ApiParam(value = "openid", required = true) String openid,
            @RequestParam("body") @ApiParam(value = "描述", required = true) String body,
            @RequestParam("id") @ApiParam(value = "主键", required = true) String id
    ) throws IOException {
        L.info("回调地址：{}",baseNotifyUrl + notify_url);
        L.info("参数：{}", money+"#"+openid+"#"+body+"#"+id);
        Map<String,String> data = new HashMap<String,String>();
        Map<String,Object> map = new HashMap<String,Object>();
        data.put("appid", Configure.getAppID());//公众账号ID
        data.put("mch_id", Configure.getMch_id());//商户号
        data.put("nonce_str", RandomStringGenerator.getRandomStringByLength(32));//随机字符串
        data.put("openid", openid);//用户标识
        data.put("body", body);//商品描述
        data.put("out_trade_no", id + "_" + RandomStringGenerator.getRandomStringByLength(18));//商户订单号
        data.put("attach", id);//
        data.put("fee_type", "CNY");//
        data.put("total_fee", money);//标价金额
        data.put("spbill_create_ip", getUserIP.getUserIP(request));//终端IP
        data.put("notify_url", baseNotifyUrl + notify_url);//回调地址
        data.put("trade_type", trade_type);  //交易类型
        //生成签名
        String signXml= "";
        try {
            //调用demo里面的sign生成方法
            signXml = WXPayUtil.generateSignedXml(data, Configure.getKey(), WXPayConstants.SignType.MD5);
            String result = HttpRequest.sendPost(url, signXml);
            System.out.println(result);
            XStream xStream = new XStream();
            xStream.setupDefaultSecurity(xStream);
            //出于安全考虑，这里必须限制类型，不然会报错
            xStream.allowTypes(new Class[]{OrderReturnInfo.class});
            xStream.alias("xml", OrderReturnInfo.class);

            OrderReturnInfo returnInfo = (OrderReturnInfo)xStream.fromXML(result);
            // 二次签名
            if ("SUCCESS".equals(returnInfo.getReturn_code()) && returnInfo.getReturn_code().equals(returnInfo.getResult_code())) {
                long time = System.currentTimeMillis()/1000;

                //生成签名（官方给出来的签名方法）
                Map<String,String> map2 = new HashMap<String,String>();
                map2.put("appId", Configure.getAppID());
                map2.put("timeStamp", String.valueOf(time));
                //这边的随机字符串必须是第一次生成sign时，微信返回的随机字符串，不然小程序支付时会报签名错误
                map2.put("nonceStr", returnInfo.getNonce_str());
                map2.put("package", "prepay_id=" + returnInfo.getPrepay_id());
                map2.put("signType", "MD5");

                String sign2 = WXPayUtil.generateSignature(map2, Configure.getKey(), WXPayConstants.SignType.MD5);
                System.out.println("二次签名的sign2----->"+sign2);
                //无效的签名方法
                //String sign1 = Signature.getSign(signInfo);
                Map<String,Object> payInfo = new HashMap<String,Object>();
                payInfo.put("timeStamp", String.valueOf(time));
                payInfo.put("nonceStr", returnInfo.getNonce_str());
                payInfo.put("prepay_id",returnInfo.getPrepay_id());
                payInfo.put("signType", "MD5");
                payInfo.put("paySign", sign2);
//                map.put("status", 200);
//                map.put("msg", "统一下单成功!");
//                map.put("data", payInfo);

                // 此处可以写唤起支付前的业务逻辑

                // 业务逻辑结束
                return Result.<Map<String,Object>>instance(200,"下单成功",payInfo).toJsonMap();
            }
            map.put("status", 500);
            map.put("msg", "统一下单失败!");
            map.put("data", returnInfo);
            return Result.<OrderReturnInfo>instance(500,"统一下单失败",returnInfo).toJsonMap();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.<Map<String,Object>>instance(500,"统一下单失败",map).toJsonMap();
    }

    @RequestMapping(value = "/weixin/wages/callback/wxNotify",method = RequestMethod.POST)
    public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception{
        L.info("进入回调");
        boolean state = false;
        BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream)request.getInputStream()));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while((line = br.readLine()) != null){
            sb.append(line);
        }
        br.close();
        //sb为微信返回的xml
        String notityXml = sb.toString();
        String resXml = "";

        L.info("接收到的报文：{}",notityXml);
        Map<String, String> resultMap = WXPayUtil.xmlToMap(notityXml);;

        // 签名验证
        if (WXPayUtil.isSignatureValid(notityXml,Configure.getKey() )) {
            // 业务结果
            String result_code = resultMap.get("result_code");
            if (result_code.equals(WXPayConstants.SUCCESS)) {
                // 订单号
                String out_trade_no = resultMap.get("out_trade_no");
                String transaction_id = resultMap.get("transaction_id");
                String attach = resultMap.get("attach");
                Wages wages = new Wages();
                wages.setId(Long.parseLong(attach));
                wages.setOutTradeNo(out_trade_no);
                wages.setTransactionId(transaction_id);
                wages.setPayStatus(1);
                wagesService.updateByPrimaryKeySelective(wages);
                L.info("微信支付订单号（transaction_id）：{}",transaction_id);
                L.info("商家数据包（attach）：{}",attach);
                L.info("订单号（out_trade_no）：{}",out_trade_no);
                // 支付成功处理业务逻辑
                state =true;//根据订单号查询自己数据库该条数据进行业务操作,操作成功返回true
            }
            if (state) {
                //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
            } else {
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
            }
        } else {
            // 签名验证失败
            L.info("签名验证失败");
        }

        L.info("返回的报文：{}",resXml);
        L.info("微信支付回调数据结束");

        BufferedOutputStream out = new BufferedOutputStream(
                response.getOutputStream());
        out.write(resXml.getBytes());
        out.flush();
        out.close();
    }




    @ApiOperation("购买会员发起微信支付")
    @ApiResponses({
            @ApiResponse(code = 200,message = "查询成功"),
            @ApiResponse(code = 500,message = "查询失败")
    })
    @RequestMapping(value = "/weixin/member/pay",method = RequestMethod.GET)
    public Map wxPayToMember(
            HttpServletRequest request,
            @RequestParam("money") @ApiParam(value = "现金数量", required = true) String money,
            @RequestParam("openid") @ApiParam(value = "openid", required = true) String openid,
            @RequestParam("body") @ApiParam(value = "描述", required = true) String body
    ) throws IOException {
        L.info("回调地址：{}",baseNotifyUrl + notify_url2);
        L.info("参数：{}", money+"#"+openid+"#"+body+"#");
        Map<String,String> data = new HashMap<String,String>();
        Map<String,Object> map = new HashMap<String,Object>();
        data.put("appid", Configure.getAppID());//公众账号ID
        data.put("mch_id", Configure.getMch_id());//商户号
        data.put("nonce_str", RandomStringGenerator.getRandomStringByLength(32));//随机字符串
        data.put("openid", openid);//用户标识
        data.put("body", body);//商品描述
        data.put("out_trade_no", RandomStringGenerator.getRandomStringByLength(25));//商户订单号
//        data.put("attach", id);
        data.put("fee_type", "CNY");//
        data.put("total_fee", money);//标价金额
        data.put("spbill_create_ip", getUserIP.getUserIP(request));//终端IP
        data.put("notify_url", baseNotifyUrl + notify_url2);//回调地址
        data.put("trade_type", trade_type);  //交易类型
        //生成签名
        String signXml= "";
        try {
            //调用demo里面的sign生成方法
            signXml = WXPayUtil.generateSignedXml(data, Configure.getKey(), WXPayConstants.SignType.MD5);
            String result = HttpRequest.sendPost(url, signXml);
            System.out.println(result);
            XStream xStream = new XStream();
            xStream.setupDefaultSecurity(xStream);
            //出于安全考虑，这里必须限制类型，不然会报错
            xStream.allowTypes(new Class[]{OrderReturnInfo.class});
            xStream.alias("xml", OrderReturnInfo.class);

            OrderReturnInfo returnInfo = (OrderReturnInfo)xStream.fromXML(result);
            // 二次签名
            if ("SUCCESS".equals(returnInfo.getReturn_code()) && returnInfo.getReturn_code().equals(returnInfo.getResult_code())) {
                long time = System.currentTimeMillis()/1000;

                //生成签名（官方给出来的签名方法）
                Map<String,String> map2 = new HashMap<String,String>();
                map2.put("appId", Configure.getAppID());
                map2.put("timeStamp", String.valueOf(time));
                //这边的随机字符串必须是第一次生成sign时，微信返回的随机字符串，不然小程序支付时会报签名错误
                map2.put("nonceStr", returnInfo.getNonce_str());
                map2.put("package", "prepay_id=" + returnInfo.getPrepay_id());
                map2.put("signType", "MD5");

                String sign2 = WXPayUtil.generateSignature(map2, Configure.getKey(), WXPayConstants.SignType.MD5);
                System.out.println("二次签名的sign2----->"+sign2);
                //无效的签名方法
                //String sign1 = Signature.getSign(signInfo);
                Map<String,Object> payInfo = new HashMap<String,Object>();
                payInfo.put("timeStamp", String.valueOf(time));
                payInfo.put("nonceStr", returnInfo.getNonce_str());
                payInfo.put("prepay_id",returnInfo.getPrepay_id());
                payInfo.put("signType", "MD5");
                payInfo.put("paySign", sign2);
//                map.put("status", 200);
//                map.put("msg", "统一下单成功!");
//                map.put("data", payInfo);

                // 此处可以写唤起支付前的业务逻辑

                // 业务逻辑结束
                return Result.<Map<String,Object>>instance(200,"下单成功",payInfo).toJsonMap();
            }
            map.put("status", 500);
            map.put("msg", "统一下单失败!");
            map.put("data", returnInfo);
            return Result.<OrderReturnInfo>instance(500,"统一下单失败",returnInfo).toJsonMap();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.<Map<String,Object>>instance(500,"统一下单失败",map).toJsonMap();
    }

    @RequestMapping(value = "/weixin/member/callback/wxNotify",method = RequestMethod.POST)
    public void wxNotifyMember(HttpServletRequest request, HttpServletResponse response) throws Exception{
        L.info("进入回调");
        boolean state = false;
        BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream)request.getInputStream()));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while((line = br.readLine()) != null){
            sb.append(line);
        }
        br.close();
        //sb为微信返回的xml
        String notityXml = sb.toString();
        String resXml = "";

        L.info("接收到的报文：{}",notityXml);
        Map<String, String> resultMap = WXPayUtil.xmlToMap(notityXml);;

        // 签名验证
        if (WXPayUtil.isSignatureValid(notityXml,Configure.getKey() )) {
            // 业务结果
            String result_code = resultMap.get("result_code");
            if (result_code.equals(WXPayConstants.SUCCESS)) {
                // 订单号
                String out_trade_no = resultMap.get("out_trade_no");
                String transaction_id = resultMap.get("transaction_id");
//                String attach = resultMap.get("attach");
                L.info("微信支付订单号（transaction_id）：{}",transaction_id);
//                L.info("商家数据包（attach）：{}",attach);
                L.info("订单号（out_trade_no）：{}",out_trade_no);
                // 支付成功处理业务逻辑
                state =true;//根据订单号查询自己数据库该条数据进行业务操作,操作成功返回true
            }
            if (state) {
                //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
            } else {
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
            }
        } else {
            // 签名验证失败
            L.info("签名验证失败");
        }

        L.info("返回的报文：{}",resXml);
        L.info("微信支付回调数据结束");

        BufferedOutputStream out = new BufferedOutputStream(
                response.getOutputStream());
        out.write(resXml.getBytes());
        out.flush();
        out.close();
    }

}
